/******************************
 * 
 * This is the main program of "Word memory assistant".
 * Author：_int_me
 * last modify: 20.8.15.17:00
 * 
******************************/

#include "headers.hpp"
using namespace std;

set <int> newpad;               //生词表

set <int> strangepad;              //生词之生词

set <int> wordst[6005];         //联想集合列表

map <string, int> wdll1;        //英文映射

class oneword
{
public:
	string en;				//英文
	string pos;				//词性
	string ch;				//中文释义
	bool isstranger = false;     //是否是生词
    int wdstn;
    void show()
    {
        cout << '\n' << "***************word***************" << '\n' << '\n';
        cout << "English: " << en << '\n';
        cout << "MainPOS: " << pos << '\n';
        cout << "Chinese: " << ch << '\n';
        if (isstranger)
        {
            cout << "It's a strange word" << '\n';
        }
        else
        {
            cout << "It's a simple word" << '\n';
        }
        cout << '\n' << "***************word***************" << '\n' << '\n';
    }
}wdlist[6100];      //单词表

int wcount = 0;     //单词总数

int setcount = 0;   //集合的数量

int setnum = 0;     //集合关系数量

void Build();       //建立单词表

void query();       //查词功能

void memory();      //记忆单词

void check(set<int> &newp);//查看生词表或措辞表

int word_operate(int mnum);              //记词操作界面

int merge(int k1, int k2, int setn);     //集合合并函数

void sscopy(char *s1, char *s2, int l, int r);//字符串拷贝

void practicenew(set<int> &newp); //练习生词

int judge(string inputt);//判断输入合法性

void saving();          //保存生词本

void jump(int &nnum);   //跳转到某个单词

int main()
{
    ifstream fin1;

    Build();
    cout << "***************Words-memory assistant***************" << '\n'; // 简洁用户界面

    while (1)
    {
        cout << "Please input a number that symbolize your want:" << '\n';
        cout << "(1)Query" << '\n';
        cout << "(2)Continue your memory" << '\n';
        cout << "(3)Check your new words notebook" << '\n';
        cout << "(4)Practice with your new words notebook" << '\n';
        cout << "(5)check your error words notebook" << '\n';
        cout << "(6)Practice with your error words notebook" << '\n';
        cout << "(7)save your notebook" << '\n';
        cout << "(0)Exit" << '\n';
        cout << "input:";
        string inputt;
        cin >> inputt;
        int n = judge(inputt);
        switch (n)              //入口
        {
            case 1:
                query();
                break;
            case 2:
                memory();
                break;
            case 3:
                check(newpad);
                break;
            case 4:
                practicenew(newpad);
                break;
            case 5:
                check(strangepad);
                break;
            case 6:
                practicenew(strangepad);
                break;
            case 7:
                saving();
                break;
            case 0:
                saving();
                return 0;
                break;
            default:
                break;
        }
    }
    
    return 0;
}

void Build()
{
    //第一步：建立wordlist

    char ss[305] = {0};
    ifstream fin2;
	fin2.open("./txt/Cet65.txt");
    int i = 0;
    while (fin2.getline(ss, 304))
	{
        ++i;
        int k = 0;
        int j = 0;
        while (1)
        {
            if (ss[k] == ' ')
            {
                break;
            }
            ++k;
        }
        char ss2[105];
        sscopy(ss, ss2, j, k - 1);
        wdlist[i].en = ss2;		 //获得当前行英文单词

        ++k;
        j = k;
        while (ss[k] != '.')
        {
            ++k;
        }
        sscopy(ss, ss2, j, k);			
        wdlist[i].pos = ss2;    //获得词性
        ++k;

        wdlist[i].ch = (char *)(ss + k);   //获得中文释义
        wdll1[wdlist[i].en] = i;
    }
    fin2.close();

    //第二步：建立assemble
    ifstream finn;
    finn.open("./txt/setnum.txt");
    fin2.open("./txt/asse.set");
    finn >> setnum;
    setcount = 0;
    int k1, k2;
    for (int j = 1; j <= setnum; ++j)
    {
        fin2 >> k1 >> k2;                        //获得两个待合并单词编号
        setcount = merge(k1, k2, setcount);
    }
    finn.close();
    fin2.close();

    //第三步，读取生词表
    fin2.open("./txt/newpad.txt");
    while (fin2.getline(ss, 304))
	{
        if (ss[0] < 65 || ss[0] > 122 || strlen(ss) < 5)
        {
            continue;
        }
        int k = 0;
        int j = 0;
        while (1)
        {
            if (ss[k] == ' ')
            {
                break;
            }
            ++k;
        }
        char ss2[105];
        sscopy(ss, ss2, j, k - 1);
        string newword = ss2;
        wdlist[wdll1[newword]].isstranger = true;
        newpad.insert(wdll1[newword]);
    }
    fin2.close();

    //读取错词表
    fin2.open("./txt/strangepad.txt");
    while (fin2.getline(ss, 304))
	{
        if (ss[0] < 65 || ss[0] > 122 || strlen(ss) < 5)
        {
            continue;
        }
        int k = 0;
        int j = 0;
        while (1)
        {
            if (ss[k] == ' ')
            {
                break;
            }
            ++k;
        }
        char ss2[105];
        sscopy(ss, ss2, j, k - 1);
        string newword = ss2;
        wdlist[wdll1[newword]].isstranger = true;
        strangepad.insert(wdll1[newword]);
    }
    fin2.close();

    wcount = i;
}

void query()
{
    string quit = "Exit";
    while (1)
    {
        cout << "Please input the word(Inputing \"Exit\" means back.): ";
        string word;
        cin >> word;
        if (word == quit)
        {
            return;
        }
        else
        {
            if (wdll1.count(word) == 1)
            {
                wdlist[wdll1[word]].show();
                cout << "Because you are in querying so you can't read other word" << '\n';
                word_operate(wdll1[word]);
            }
            else
            {
                cout << "Sorry, no such word" << '\n';
            }
        }
    }
}

void memory()
{
    ifstream fin2;
    fin2.open("./txt/pastnum.txt");
    int num;
    fin2 >> num;
    fin2.close();
    cout << "Do you want not to start at the last word?[Y/N]" << '\n';
    char ss;
    cin >> ss;
    if (ss == 'y' || ss == 'y' - 32)
    {
        jump(num);
    }
    int lastnum = -1;
    while (1)
    {
        if (lastnum != num)
        {
            wdlist[num].show();
            cout << '[' << num << ']' << '\n';
        }

        lastnum = num;
        num = word_operate(num);

        if (num == -1)
        {
            cout << "exit" << '\n';
            ofstream fnum;
            fnum.open("./txt/pastnum.txt");
            fnum << lastnum;
            fnum.close();
            break;
        }

        if (num == -2)
        {
            num = lastnum;
        }

        if (num == 0)
        {
            cout << "no word" << '\n';
            ++num;
        }
        if (num == wcount + 1)
        {
            cout << "no word" << '\n';
            --num;
        }
    }
}

int word_operate(int mnum)
{
    cout  << '\n' << "(1)last / (2)next" << '\n';
    cout << "(3)add to new pad / (4)remove from new pad" << '\n';
    cout << "(5)add to a set / (6)show the set" << '\n';
    cout << "(7)remove from strange pad / (8)jump to other word" << '\n';
    cout << "(0)exit" << '\n' << '\n';
    string wantc;
    cin >> wantc;
    int want = judge(wantc);
    switch (want)
    {
        case 1:
            --mnum;
            break;
        case 2:
            ++mnum;
            break;
        case 3:
            wdlist[mnum].isstranger = true;
            newpad.insert(mnum);
            cout << "Successfully add " << wdlist[mnum].en << " to the new pad!" << '\n';
            break;
        case 4:
            wdlist[mnum].isstranger = false;
            newpad.erase(mnum);
            cout << "Successfully remove " << wdlist[mnum].en << " from the new pad!" << '\n';
            return -2;
            break;
        case 5:
        {
            cout << "Please input a word added to the set(input q to exit):";
            string setword;
            string quit1 = "q";
            char ss;
            cin >> setword;
            if (setword == quit1)
            {
                break;
            }
            if (wdll1.count(setword) == 1)
            {
                wdlist[wdll1[setword]].show();
                if (wdlist[wdll1[setword]].wdstn == wdlist[mnum].wdstn && wdlist[mnum].wdstn != 0)
                {
                    cout << "This word has been in the current set" << '\n';
                    break;
                }
                cout << "Do you want to add it to the set?[Y/N]" << '\n';
                cin >> ss;
                if (ss == 'y' || ss == 'y' - 32)
                {
                    setcount = merge(wdll1[setword], mnum, setcount);
                    ++setnum;
                    ofstream fout1;                                                 //打开集合写入
                    ofstream fout2;
                    fout1.open("./txt/asse.set", ios::app);
                    fout2.open("./txt/setnum.txt");
                    fout1 << wdll1[setword] << ' ' << mnum << '\n';       //保存操作
                    fout2 << setnum;
                    fout1.close();
                    fout2.close();
                    cout << "Successfully add " << setword << " to the set!" << '\n';
                }
            }
            else
            {
                cout << "Sorry, no such word" << '\n';
            }
            break;
        }
        case 6:
            if (!wordst[wdlist[mnum].wdstn].size())
            {
                cout << "This word'set has no other words";
            }
            else
            {
                int co = 1;
                for (set<int>::iterator it = wordst[wdlist[mnum].wdstn].begin(); it != wordst[wdlist[mnum].wdstn].end(); ++it)
                {
                    cout << '\n' << co << ':' << '\n';
                    wdlist[*it].show();
                    ++co;
                }
            }
            
            break;
        case 7:
            {
                strangepad.erase(mnum);
                cout << "Successfully remove " << wdlist[mnum].en << " from the strange pad!" << '\n';
                return -2;
                break;
            }
        case 8:
            jump(mnum);
            break;
        case 0:
            return -1;
            break;
        default:
            break;
    }
    return mnum;
}

int merge(int k1, int k2, int setn)                  //分情况合并集合
{
    int s1 = wdlist[k1].wdstn;
    int s2 = wdlist[k2].wdstn;              //获得各自所在集合的编号（不在集合则为0）
    if (s1 && s2)
    {
        wordst[s1].insert(wordst[s2].begin(), wordst[s2].end());
        for (set<int>::iterator it = wordst[s2].begin(); it != wordst[s2].end(); ++it)
        {
            wdlist[*it].wdstn = s1;
        }
    }
    else if (s1 && !s2)
    {
        wordst[s1].insert(k2);
        wdlist[k2].wdstn = s1;
    }
    else if (!s1 && s2)
    {
        wordst[s2].insert(k1);
        wdlist[k1].wdstn = s2;
    }
    else
    {
        wordst[++setn].insert(k1);
        wordst[setn].insert(k2);
        wdlist[k1].wdstn = setn;
        wdlist[k2].wdstn = setn;
    }
    return setn;
}

void sscopy(char* s1, char* s2, int l, int r)
{
	for (int i = 0; i + l <= r; ++i)
	{
		s2[i] = s1[l + i];
	}
	s2[r - l + 1] = '\0';
}

void practicenew(set<int> &newp)
{
    if (!newp.size())
    {
        cout << "Now there is no word" << '\n';
    }
    else
    {
        set<int>::iterator it = newp.begin();
        set<int>::iterator it2 = newp.end();
        set<int>::iterator it3 = newp.end();
        int ll = 1;
        while (1)
        {
            if (it != it2)
            {
                it2 = it;
                cout << '\n' << '[' << ll << ']' << '\n';

                cout << "***************word***************" << '\n' << '\n';
                cout << "English: " << wdlist[*it].en << '\n';
                cout << '\n' << "***************word***************" << '\n' << '\n';

                cout  << "KNOW?[(Y/2)/(N/3)](input q to exit, 1 to back to the last word)" << '\n';
            
                string cc;
                cin >> cc;
                if (cc == "q")
                {
                    return;
                }
                if (cc == "1")
                {
                    it3 = it;
                    if (it == newp.begin())
                    {
                        cout << "no word" << '\n';
                    }
                    else
                    {
                        --it;
                        --ll;
                    }
                    continue;
                }
                if (cc == "Y" || cc == "y" || cc == "2")
                {
                    it3 = it;
                    ++it;
                    if (it == newp.end())
                    {
                        cout << "no word" << '\n';
                        --it;
                    }
                    else
                    {
                        ++ll;
                    }
                    continue;
                }

                if (cc == "n" || cc == "N" || cc == "3")
                {
                    strangepad.insert(*it);
                    cout << "the word has been insert into error notepad" << '\n';
                }
            }

            if (it3 != it)
            {
                wdlist[*it].show();
            }
            
            int pre = 0;
            int nex = 0;

            if (it != newp.begin())
            {
                --it;
                pre = *it;
                ++it;
            }
            if (++it != newp.end())
            {
                nex = *(it);
            }
            --it;

            cout << '[' << ll << ']' << '\n';

            int newnum = word_operate(*it);

            if (newnum == (*it) + 1 || newnum == -1) //modification
            {
                it3 = it;
                ++it;
                if (it == newp.end())
                {
                    cout << "no word" << '\n';
                    --it;
                }
                else
                {
                    ++ll;
                }
                
            }
            else if (newnum == (*it) - 1)
            {
                it3 = it;
                if (it == newp.begin())
                {
                    cout << "no word" << '\n';
                }
                else
                {
                    --it;
                    --ll;
                }
                
            }
/*                                              //modification
            else if (newnum == -1)
            {
                cout << "exit" << '\n';
                break;
            }
*/
            else if (newnum == -2)              //更正集合中指针的偏移
            {
                it3 = it;
                ++it;
                int cc = 0;
                if (!pre && !nex)
                {
                    it = newp.end();
                    continue;
                }
                else if (pre && !nex)
                {
                    cc = pre;
                    --ll;
                }
                else
                {
                    cc = nex;
                }

                while (*it > cc)
                {
                    --it;
                }
                while (*it < cc)
                {
                    ++it;
                }
            }

            else
            {
                ;
            }
        }
    }
}

void check(set<int> &newp)
{
    if (!newp.size())
    {
        cout << "Now there is no word" << '\n';
    }
    else
    {
        set<int>::iterator it = newp.begin();
        set<int>::iterator it2 = newp.end();
        int ll = 1;
        while (1)
        {
            
            if (it != it2)
            {
                wdlist[*it].show();
                it2 = it;
            }

            int pre = 0;
            int nex = 0;

            if (it != newp.begin())
            {
                --it;
                pre = *it;
                ++it;
            }
            if (++it != newp.end())
            {
                nex = *(it);
            }
            --it;

            cout << '[' << ll << ']' << '\n';

            int newnum = word_operate(*it);

            if (newnum == (*it) + 1)
            {
                ++it;
                if (it == newp.end())
                {
                    cout << "no word" << '\n';
                    --it;
                }
                else
                {
                    ++ll;
                }
                
            }
            else if (newnum == (*it) - 1)
            {
                if (it == newp.begin())
                {
                    cout << "no word" << '\n';
                }
                else
                {
                    --it;
                    --ll;
                }
                
            }

            else if (newnum == -1)
            {
                cout << "exit" << '\n';
                break;
            }

            else if (newnum == -2)
            {
                ++it;
                int cc = 0;
                if (!pre && !nex)
                {
                    it = newp.end();
                    continue;
                }
                else if (pre && !nex)
                {
                    cc = pre;
                    --ll;
                }
                else
                {
                    cc = nex;
                }

                while (*it > cc)
                {
                    --it;
                }
                while (*it < cc)
                {
                    ++it;
                }
            }

            else
            {
                ;
            }
        }
    }
}

void saving()
{
    ofstream newpadf;
    newpadf.open("./txt/newpad.txt");
    for (set<int>::iterator it = newpad.begin(); it != newpad.end(); ++it)
    {
        newpadf << wdlist[*it].en << ' ';
        newpadf << wdlist[*it].pos << ' ';
        newpadf << wdlist[*it].ch << '\n' << '\n';
    }
    newpadf.close();
    newpadf.open("./txt/strangepad.txt");
    for (set<int>::iterator it = strangepad.begin(); it != strangepad.end(); ++it)
    {
        newpadf << wdlist[*it].en << ' ';
        newpadf << wdlist[*it].pos << ' ';
        newpadf << wdlist[*it].ch << '\n' << '\n';
    }
    newpadf.close();
    cout << "Successfully save your words to the \"newpad.txt\" and \"strangepad.txt\"!" << '\n';
}

void jump(int& nnum)
{
    string word;
    cout << "Please choose input the word or number(W or N)\n(Inputing other keys means giving up.):";
    cin >> word;
    if (word == "W" || word == "w")
    {
        while (1)
        {
            cout << "Please input the word(Inputing \"q\" means giving up.):";
            cin >> word;
            if (wdll1.count(word) == 1)
            {
                nnum = wdll1[word];
                break;
            }
            else
            {
                if (word == "q")
                {
                    break;
                }
                cout << "Sorry, no such word" << '\n';
            }
        }
    }
    else if (word == "N" || word == "n")
    {
        while (1)
        {
            int nu = 0;
            cout << "Please input the number(Inputing \"0\" means giving up.):";
            cin >> nu;
            if (nu > 0 && nu <= wcount)
            {
                nnum = nu;
                break;
            }
            else
            {
                if (!nu)
                {
                    break;
                }
                cout << "Sorry, no such number" << '\n';
            }
        }
    }
    else
    {
        ;
    }
}

int judge(string inputt)
{
    if (inputt == "1")
    {
        return 1;
    }
    else if (inputt == "2")
    {
        return 2;
    }
    else if (inputt == "3")
    {
        return 3;
    }
    else if (inputt == "4")
    {
        return 4;
    }
    else if (inputt == "5")
    {
        return 5;
    }
    else if (inputt == "6")
    {
        return 6;
    }
    else if (inputt == "7")
    {
        return 7;
    }
    else if (inputt == "8")
    {
        return 8;
    }
    else if (inputt == "9")
    {
        return 9;
    }
    else if (inputt == "0")
    {
        return 0;
    }
    else
    {
        cout << "Sorry, error input" << '\n';
        return -1;
    }
}
